#include <main.h>

#define SDA P1_1
#define SCL P1_0

typedef enum {
	type_command = 0,
	type_data
} IICType;

byte curRow = 0, curCol = 0;
signed char lineMode = 0;

void iicStart() {
	SCL = 0;
	SDA = 1;
	SCL = 1;
	SDA = 0;
	SCL = 0;
}

void iicStop() {
	SCL = 0;
	SDA = 0;
	SCL = 1;
	SDA = 1;
}

byte iicAck() {
	SCL = 0;
	SDA = 1;
	SCL = 1;
	return SDA == 0;
}

void iicSend(byte data) {
	for (byte i = 8; i > 0; i--) {
		SCL = 0;
		SDA = (data >> (i - 1)) & 1;
		SCL = 1;
	}
}

void sendData(byte data, IICType mode)
{
	iicStart();
	iicSend(0x78);
	if (!iicAck()) {
		iicStop();
		return;
	}
	if (mode == type_data)
	{
		iicSend(0x40);
	}
	else
	{
		iicSend(0x00);
	}
	if (!iicAck()) {
		iicStop();
		return;
	}
	iicSend(data);
	iicAck();
	iicStop();
}

void initOled()
{
	sendData(0xAE, type_command); //--turn off oled panel
	sendData(0x00, type_command); //---set low column address
	sendData(0x10, type_command); //---set high column address 
	sendData(0x40, type_command); //--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
  sendData(0xB0, type_command);
	sendData(0x81, type_command); //--set contrast control register!!!!!!!!
	sendData(0xCF, type_command); // Set SEG Output Current Brightness
	sendData(0xA1, type_command); //--Set SEG/Column Mapping     0xa0×óÓÒ·´ÖÃ 0xa1Õý³£
	sendData(0xC8, type_command); // Set COM/Row Scan Direction   0xc0ÉÏÏÂ·´ÖÃ 0xc8Õý³£
	sendData(0xA6, type_command); //--set normal display
	sendData(0xA8, type_command); //--set multiplex ratio(1 to 64)
	sendData(0x3f, type_command); //--1/64 duty
	sendData(0xD3, type_command); //-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
	sendData(0x00, type_command); //-not offset
	sendData(0xd5, type_command); //--set display clock divide ratio/oscillator frequency
	sendData(0xC0, type_command); //--set divide ratio, Set Clock as 100 Frames/Sec
	sendData(0xD9, type_command); //--set pre-charge period
	sendData(0xF1, type_command); // Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
	sendData(0xDA, type_command); //--set com pins hardware configuration
	sendData(0x12, type_command);
	sendData(0xDB, type_command); //--set vcomh
	sendData(0x30, type_command); // Set VCOM Deselect Level
	sendData(0x20, type_command); //-Set Page Addressing Mode (0x00/0x01/0x02)
	sendData(0x02, type_command); //
	sendData(0x8D, type_command); //--set Charge Pump enable/disable
	sendData(0x14, type_command); //--set(0x10) disable
	sendData(0xAF, type_command);
}

void selectOledPos(byte page, byte col) {
	sendData(0xb0 + page, type_command);
	sendData(0x10 | ((col >> 4) & 0xf), type_command);
	sendData(col & 0xf, type_command);
}

void clearScreen() {
	curRow = 0;
	curCol = 0;
	for (byte page = 0; page < 8; page++) {
		selectOledPos(page, 0);
		for (byte col = 0; col < 128; col++) {
			sendData(0, type_data);
		}
	}
}

void drawPoint() {
	static byte pointCol = 0;
	static signed char lastLineMode = 0;
	static byte isFirst = 1;
	if (isFirst) {
		lastLineMode = lineMode;
		isFirst = 0;
	}
	if (lastLineMode != lineMode) {
		if (lastLineMode == 1) {
			selectOledPos(0, pointCol);
			sendData(0xf8, type_data);
			selectOledPos(1, pointCol);
			sendData(0xff, type_data);
			selectOledPos(2, pointCol);
			sendData(0xff, type_data);
			selectOledPos(3, pointCol);
			sendData(0xff, type_data);
			selectOledPos(4, pointCol);
			sendData(0xf, type_data);
		} else if (lastLineMode == -1) {
			selectOledPos(4, pointCol);
			sendData(0xf8, type_data);
			selectOledPos(5, pointCol);
			sendData(0xff, type_data);
			selectOledPos(6, pointCol);
			sendData(0xff, type_data);
			selectOledPos(7, pointCol);
			sendData(0xf, type_data);
		}
		lastLineMode = lineMode;
		if (lineMode == 1) {
			selectOledPos(0, pointCol);
			sendData(0xf8, type_data);
			selectOledPos(1, pointCol);
			sendData(0xff, type_data);
			selectOledPos(2, pointCol);
			sendData(0xff, type_data);
			selectOledPos(3, pointCol);
			sendData(0xff, type_data);
			selectOledPos(4, pointCol);
			sendData(0xf, type_data);
		} else if (lineMode == -1) {
			selectOledPos(4, pointCol);
			sendData(0xf8, type_data);
			selectOledPos(5, pointCol);
			sendData(0xff, type_data);
			selectOledPos(6, pointCol);
			sendData(0xff, type_data);
			selectOledPos(7, pointCol);
			sendData(0xf, type_data);
		}
	}
	pointCol++;
	if (pointCol == 128) {
		pointCol = 0;
	}
	selectOledPos(0, pointCol);
	sendData(0, type_data);
	selectOledPos(1, pointCol);
	sendData(0, type_data);
	selectOledPos(2, pointCol);
	sendData(0, type_data);
	selectOledPos(3, pointCol);
	sendData(0, type_data);
	selectOledPos(4, pointCol);
	sendData(0, type_data);
	selectOledPos(5, pointCol);
	sendData(0, type_data);
	selectOledPos(6, pointCol);
	sendData(0, type_data);
	selectOledPos(7, pointCol);
	sendData(0, type_data);
	if (lineMode == 0) {
		selectOledPos(4, pointCol);
	} else if (lineMode == 1) {
		selectOledPos(0, pointCol);
	} else if (lineMode == -1) {
		selectOledPos(7, pointCol);
	}
	sendData(8, type_data);
} 

void putchar(char ch) {
	if (ch == '\n') {
		curRow++;
		curCol = 0;
	} else if (ch == ' ') {
		curCol += 8;
	} else {
		if (ch >= '0' && ch <= '9') {
			selectOledPos(curRow, curCol);
			curCol += 8;
			for (byte i = 0; i < 8; i++) {
				sendData(characters[ch - '0'][i], type_data);
			}
		}
	}
}

void printNum(u32 num, u8 width) {
	u32 temp = 0;
	u32 zeroCnt = 0;
	if (num == 0) {
		if (width != 0) {
			width--;
			while (width--) {
				putchar('0');
			}
		}
		putchar('0');
		return;
	}
	while (num > 0) {
		temp = temp * 10 + num % 10;
		if (temp == 0 && num % 10 == 0) {
			zeroCnt++;
		}
		if (width != 0) {
			width--;
		}
		num /= 10;
	}
	while (width--) {
		putchar('0');
	}
	while (temp > 0) {
		putchar(temp % 10 + 0x30);
		temp /= 10;
	}
	while (zeroCnt--) {
		putchar('0');
	}
}

void main() {
	initOled();
	clearScreen();
	while (1) {
		if (P0_0 == 0 && P0_1 == 1) {
			lineMode = 1;
		} else if (P0_0 == 1 && P0_1 == 0) {
			lineMode = -1;
		} else {
			lineMode = 0;
		}
		drawPoint();
	}
}